#include "demoopenglwidget.h"
#include"QDebug"
#include<QStringList>
#include <QFile>
#include<QtMath>

demoOpenglWidget::demoOpenglWidget(QWidget *parent)
    : QOpenGLWidget(parent),
      VBO(QOpenGLBuffer::VertexBuffer),
      xtrans(0),ytrans(0),ztrans(0.0),
      verticesCnt(0)//顶点坐标计数
{
    QSurfaceFormat format;
    format.setAlphaBufferSize(24);
    format.setVersion(3,3);
    format.setSamples(10);//设置重采样次数，用于反走样

    this->setFormat(format);

    vertices=loadAscllStl(":/src/stl/Fuß-Fanny-Elssler.txt",1);//可用相对路径
//        vertices = loadBinStl("C:\\Qt\\QT\\OpenglText\\demo4\\stl\\demo_bin.stl",1);//使用绝对路径
}

demoOpenglWidget::~demoOpenglWidget()
{
    makeCurrent();
}

//渲染OpenGL场景，widget需要更新时调用
void demoOpenglWidget::initializeGL()
{
    this->initializeOpenGLFunctions();
    shaderprogram.create();
    if(!shaderprogram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/shaders/stl.vert"))
        qDebug()<<"ERROR:"<<shaderprogram.log();

    if(!shaderprogram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/shaders/stl.frag"))
        qDebug()<<"ERROR:"<<shaderprogram.log();    //如果编译出错,打印报错信息

    //将添加到此程序的着色器与addshader链接在一起
    if(!shaderprogram.link())
        qDebug()<<"ERROR:"<<shaderprogram.log();    //如果链接出错,打印报错信息


    VAO.create();
    VAO.bind();
    VBO.create();
    VBO.bind();
    VBO.allocate(vertices.data(),sizeof(float)*vertices.size());


    shaderprogram.setAttributeBuffer("aPos",GL_FLOAT,0,3,sizeof(GLfloat)*6);
    shaderprogram.enableAttributeArray("aPos");
    shaderprogram.setAttributeBuffer("aNormal", GL_FLOAT,sizeof(GLfloat) * 3, 3, sizeof(GLfloat) * 6);
    shaderprogram.enableAttributeArray("aNormal");
    this->glEnable(GL_DEPTH_TEST);

    view.setToIdentity();
    view.lookAt(QVector3D(0.0f,0.0f,3.0f),QVector3D(0.0f,0.0f,0.0f),QVector3D(0.0f,1.0f,0.0f));

}

//设置OpenGL视口、投影等。
void demoOpenglWidget::resizeGL(int w, int h)
{
    this->glViewport(0,0,w,h);
    projection.setToIdentity();
    projection.perspective(60.0f,(GLfloat)w/(GLfloat)h, 0.001f, 100.0f);//视角-宽高比例-zNear-zFar

}

//设置OenGL资源和状态；第一次调用resizeGL/paintGL调用
void demoOpenglWidget::paintGL()
{
    this->glClearColor(0.0f,0.0f,0.0f,1.0f);
    this->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    shaderprogram.bind();


    //定义参数
    QVector3D lightColor(1.0f,1.0f,1.0f);
    QVector3D objectColor(1.0f,1.0f,0.88f);//模型颜色
    QVector3D lightPos(0.0f,0.0f,50.0f);
    //传值到小程序
    shaderprogram.setUniformValue("objectColor",objectColor);
    shaderprogram.setUniformValue("lightColor",lightColor);
    shaderprogram.setUniformValue("lightPos", lightPos);

    model.setToIdentity();
    model.translate(xtrans,ytrans,ztrans);
    model.rotate(rotation);
    shaderprogram.setUniformValue("view",view);
    shaderprogram.setUniformValue("projection", projection);
    shaderprogram.setUniformValue("model", model);

    int n = vertices.capacity()/sizeof(float);
    qDebug() << n;//打印stl中三角形的数量（stl是多个三角形组成的文件）
    QOpenGLVertexArrayObject::Binder bind(&VAO);//绑定VAO
    this->glDrawArrays(GL_TRIANGLES,0,n);

}

QVector<float> demoOpenglWidget::loadAscllStl(QString filename, int ratio)
{
    QVector<float> vertices_temp;
    qDebug()<<"load text file:"<<filename;

    QFile file(filename);
    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
        qDebug() << "Open stl_file failed." << endl;
    }
    while(!file.atEnd()){
        QString line = file.readLine().trimmed();//trim去除首尾空白字符
        QStringList words = line.split(' ',QString::SkipEmptyParts);

        if(!file.atEnd() && words[0]=="facet"){
            Normal = {ratio*words[2].toFloat(),ratio*words[3].toFloat(),ratio*words[4].toFloat()};
        }
        else if (!file.atEnd() && words[0] == "vertex") {
            Position = {ratio*words[1].toFloat(), ratio*words[2].toFloat(),ratio*words[3].toFloat()};
            vertices_temp.append(Position);
            vertices_temp.append(Normal);
        }
        else
            continue;
    }
    file.close();

            qDebug() << "write vertice_temp success!" << filename;
            return vertices_temp;
}

QVector<float> demoOpenglWidget::loadBinStl(std::string filename, int ratio)
{
    QVector<float> vertices_temp;
    qDebug()<<"load text file:"<<filename.c_str();

    FILE *file = fopen(filename.c_str(),"rb");

    //80字节文件头
    char header[80];
    fread(header,80,1,file);
    //4字节三角形面片数量
    uint32_t triangleNum;
    fread(&triangleNum,sizeof(uint32_t),1,file);
    float n1,n2,n3;
    float p1,p2,p3;
    for(auto i=0;i<triangleNum;i++){//读取法向量信息
        fread(&n1,sizeof(float),1,file);
        fread(&n2,sizeof(float),1,file);
        fread(&n3,sizeof(float),1,file);
        qDebug()<<"n1="<<n1<<" n2="<<n2<<" n3="<<n3;
        Normal={n1*ratio,n2*ratio,n3*ratio};

        for(auto j=0;j<3;j++){//读取顶点信息
            fread(&p1,sizeof(float),1,file);
            fread(&p2,sizeof(float),1,file);
            fread(&p3,sizeof(float),1,file);
            qDebug()<<"p1="<<p1<<" p2="<<p2<<" p3="<<p3;
            Position = {p1*ratio,p2*ratio,p3*ratio};
            vertices_temp.append(Position);
            vertices_temp.append(Normal);
        }
        //2字节三角形面片属性
        char c[2];
        fread(c,2,1,file);
    }
    fclose(file);
    delete file;
    qDebug() << "write vertice_temp success!" << filename.c_str();
    return vertices_temp;
}


void demoOpenglWidget::mousePressEvent(QMouseEvent *event)
{
    mousePos = QVector2D(event->pos());
    event->accept();
}

void demoOpenglWidget::mouseMoveEvent(QMouseEvent *event)
{
    if(event->buttons() == Qt::LeftButton){
        QVector2D newPos = (QVector2D)event->pos();
        QVector2D diff = newPos - mousePos;
        qreal angle = (diff.length())/3.6;

        QVector3D rotationAxis = QVector3D(diff.y(), diff.x(), 0.0).normalized();
        rotation = QQuaternion::fromAxisAndAngle(rotationAxis, angle) * rotation;
        mousePos = newPos;
        this->update();
    }
    event->accept();
}

void demoOpenglWidget::wheelEvent(QWheelEvent *event)//鼠标滚轮缩放模型
{
    QPoint numDegrees = event->angleDelta()/8;

    if(numDegrees.y() > 0){
        ztrans += 0.25f;
    }
    else if(numDegrees.y() < 0){
        ztrans -= 0.25f;
    }
    this->update();
    event->accept();
}


